package com.lhs.Service;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.HighlightField;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.util.NamedValue;
import com.lhs.Result.Result;
import com.lhs.entity.blog.article_search;
import com.lhs.entity.search.HotSearchHistory;
import com.lhs.entity.search.SearchRecord;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.autoconfigure.RocketMQAutoConfiguration;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ElasticsearchService {

    @Autowired
    private ElasticsearchClient client;

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;





    /**
     * 分页 + 高亮 + 多维排序（包含 smart 综合排序）
     *
     * @param keyword 搜索关键字
     * @param page    页码（从1开始）
     * @param size    每页数量
     * @param sortBy  排序方式：relevance/time/like/view/smart
     */
    public Result searchWithHighlight(String keyword, int page, int size, String sortBy) throws IOException {
        if (keyword == null || keyword.trim().isEmpty()) {
            log.warn("搜索关键字为空");
            return Result.success(Collections.emptyList()).setTotal(0).setPage(page).setSize(size);
        }

        if (page < 1) page = 1;
        if (size <= 0 || size > 100) size = 10; // 限制每页最大100条
        int from = (page - 1) * size;

        sortBy = (sortBy == null) ? "relevance" : sortBy.toLowerCase();

        // 基础查询
        Query baseQuery = QueryBuilders.multiMatch()
                .fields("title", "content")
                .query(keyword)
                .build()
                ._toQuery();

        Query finalQuery = baseQuery;
        SortOptions sortOption = null;

        switch (sortBy) {
            case "time" -> {
                sortOption = SortOptions.of(s -> s.field(f -> f.field("publishTime").order(SortOrder.Desc)));
            }
            case "smart" -> {
                FunctionScoreQuery functionQuery = FunctionScoreQuery.of(f -> f
                        .query(baseQuery)
                        .functions(
                                FunctionScore.of(fs -> fs.filter(Query.of(q -> q.matchAll(m -> m))).weight(1.0)),
                                FunctionScore.of(fs -> fs.fieldValueFactor(fvf -> fvf.field("likes").factor(1.0).modifier(FieldValueFactorModifier.Log1p))),
                                FunctionScore.of(fs -> fs.fieldValueFactor(fvf -> fvf.field("views").factor(0.1).modifier(FieldValueFactorModifier.Log1p)))
                        )
                        .scoreMode(FunctionScoreMode.Sum)
                        .boostMode(FunctionBoostMode.Multiply)
                );
                finalQuery = Query.of(q -> q.functionScore(functionQuery));
            }
            default -> {
                sortOption = SortOptions.of(s -> s.score(sc -> sc.order(SortOrder.Desc)));
            }
        }

        Map<String, HighlightField> highlightFields = new HashMap<>();
        highlightFields.put("title", HighlightField.of(h -> h.preTags("<em style='color:red'>").postTags("</em>")));
        highlightFields.put("content", HighlightField.of(h -> h.preTags("<em style='color:red'>").postTags("</em>")));

        SearchRequest.Builder builder = new SearchRequest.Builder()
                .index("blog_post")
                .from(from)
                .size(size)
                .query(finalQuery)
                .highlight(h -> h.fields((List<NamedValue<HighlightField>>) highlightFields));

        if (!"smart".equals(sortBy)) {
            builder.sort(sortOption);
        }

        SearchResponse<article_search> response;
        try {
            response = client.search(builder.build(), article_search.class);
        } catch (IOException e) {
            log.error("Elasticsearch 查询失败，keyword={}, page={}, size={}, sortBy={}", keyword, page, size, sortBy, e);
            throw e;
        }

        List<article_search> resultList = new ArrayList<>();
        for (Hit<article_search> hit : response.hits().hits()) {
            article_search blog = hit.source();
            if (blog == null) continue;

            Map<String, List<String>> highlight = hit.highlight();
            if (highlight != null) {
                List<String> titleHighlights = highlight.get("title");
                if (titleHighlights != null) {
                    blog.setTitle(String.join("", titleHighlights));
                }

                List<String> contentHighlights = highlight.get("content");
                if (contentHighlights != null) {
                    blog.setContent(String.join("", contentHighlights));
                }
            }
            resultList.add(blog);
        }

        long total = response.hits().total() != null ? response.hits().total().value() : 0L;

        return Result.success(resultList)
                .setTotal(total)
                .setPage(page)
                .setSize(size);
    }

    private static final String HOT_KEY = "search:hot";
        /**
     * 记录用户搜索关键字并更新热门搜索排行榜
     *
     * @param userId 用户ID
     * @param keyword 搜索关键字
     */
    public void recordSearch(long userId, String keyword) {
       // 构造分布式锁的键名，防止同一用户同一关键字重复处理
       String lockKey = "search:lock:" + userId+":"+keyword;
       // 使用Redis分布式锁，设置1分钟过期时间，避免重复处理同一用户的相同搜索
       Boolean isNew = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 1L, java.util.concurrent.TimeUnit.MINUTES);
       if (Boolean.TRUE.equals(isNew)) {
           // 更新热门搜索关键字排行榜，对应关键字分数加1
           redisTemplate.opsForZSet().incrementScore(HOT_KEY, keyword, 1);
           // 构造搜索记录并发送到消息队列进行异步处理
           SearchRecord record = new SearchRecord();
           record.setUserId(userId);
           record.setKeyword(keyword);
           record.setTime(LocalDateTime.now());
           rocketMQTemplate.convertAndSend("search", record);
       }
    }

// 获取热门搜索
/**
 * 获取热门搜索列表
 *
 * @param limit 限制返回的热门搜索数量，必须大于0
 * @return 热门搜索关键词列表，如果参数无效或无数据则返回空列表
 */
public List<String> getHotSearches(int limit) {
    // 参数校验，限制数量必须大于0
    if (limit <= 0) {
        return List.of();
    }

    // 从Redis的有序集合中获取排名前limit的热门搜索词
    Set<Object> rawHot = redisTemplate.opsForZSet().reverseRange(HOT_KEY, 0, limit - 1);
    if (rawHot == null) {
        return List.of();
    }

    // 过滤空值并转换为字符串列表
    return rawHot.stream()
            .filter(Objects::nonNull)
            .map(Object::toString)
            .collect(Collectors.toList());
}


}
